Structs, Enumerators and Interfaces are various different types/data structures that can be created in go to fulfill various needs. These three mechanisms are the closest that go gets to being object oriented BUT make no mistake, none of these types are similar to traditional object oriented programming entirely, instead they allow you to follow similar design patterns that are available in object oriented languages without any sort of real sub-classing, or any 'one-stop shop' for objects.
Some terms to help you navigate the post
Types are a way of generalizing data; for example a real number can be represented as an integer type (or int as it's commonly called). Types are found in all languages from simple types like integers, and strings to collections like arrays to even custom types like those presented in this demo.
Structs are a way of defining custom typed groupings of data (similar to class attributes). Another easy way to think about structs is to think of them is similar to a templated version of a Hash table (a dict in python).
A simple example would be a date struct:
type Date struct{
day int
month int
year int
}
You can then use this date struct as a type to store data, for example:
package main
import "fmt"
func main(){
today := Date{day: 13, month: 8, year: 2019}
// The data can then be accessed using dot syntax
fmt.printf("Today is %v-%v-%v", today.day, today.month, today.year) // prints Today is 13-8-2019
}
Because structs are types you can use them in almost any way you would use any other type such as creating an array of them, or nesting them inside each other. To demonstrate the nesting capabilities here is an example of a person struct with an attribute of birthday that is of the custom struct type Date that we defined earlier:
type Person struct{
name string
age int
birthday Date
}
Interfaces are collections of method signatures that you can assign a name to. They effectively allow you to store a set of methods that will be implemented later on, usually in the context of a struct.
For example:
// Coin is a Struct to represents coins in various currencies
type Coin struct { // NOTE: Structs are like objects that only have attributes
value float64 // How much the coin is worth
name string // The colloquial name of the coin
}
// Currency is an interface that stores Currency conversion functions
type Currency interface {
toUSD() float64
}
// toUSD takes a coin in CAD and returns the USD equivalent
func (c Coin) toUSD() float64 {
return c.value * 0.7 // 1 CAD is roughly .7 USD
}
With this interface of Currency added to the Coin struct, we can now do something like this:
toonie := Coin{value: 2.00, name: "Toonie"}
fmt.Printf("One %v is worth CAD $%v and USD $%v", toonie.name, toonie.value, toonie.toUSD()) // prints One toonie is worth CAD $2 and USD $1.4
Notice that the interface allows us to use the dot syntax to pass the instance as an argument, this is incredibly helpful for readability in programs with complicated structs and interfaces.
Enumerators are data structures that allow you to store CONSTANT information in a structured way. The idea behind enumerators is to store values that will be used throughout a program in a codified way to provide consistent behaviour. Typically enumerators represent data as key-value pairs where the value is an int. For example you could create an enumerator for various countries:
// Countries is a dummy type for the enumerator found below
type Countries int
// An enumerator of various countries
const (
Canada Countries = 0
UnitedStates Countries = 1
)
There is also a mechanism called iota built into go to make creating enumerators more simple. Iota will start as 0 right after the const() and increment on every assignment.
Here is an example of how it works:
// Countries is a dummy type for the enumerator found below
type Countries int
// An enumerator of various countries
const (
Canada Countries = iota // iota is == 0
UnitedStates Countries = iota // iota == 1
)
// The enumerator above is equivalent to
const (
Canada Countries = 0
UnitedStates Countries = 1
)
You can also create a function to print string representations of your enumerators, for example:
package main
import "fmt"
// Countries is a dummy type for the enumerator found below
type Countries int
// An enumerator of various countries
const (
Canada Countries = 0
UnitedStates Countries = 1
)
// Function takes a Countries instance as an argument and returns a string representation
func (country Countries) String() string {
// Define an array of strings to print based on the enumerator definition
names := [...]string{
"Canada", // If the country passed is equal to 0 return Canada
"United States"} // If the country passed is equal to 1 return United States
return names[country] // return the name of country
}
func main(){
// Prints the string representation of Countries(0), which is canada
fmt.Printf("My country is %s", Countries(0))
}
To run go code there are two options either:
Run go build <filename>
then run the resulting .exe(windows)/binary(mac and linux) file.
Run go run <filename>
, this will run the code by compiling and running the result but it does not save the resulting .exe(windows)/binary(mac and linux) file.
The real world applications of Structs, Enumerators and Interfaces are endless as such I will just leave you with the examples provided, and some additional resources to learn about each.
Here are some resources with more examples and information:
package main // Allows file to be run on it's own
import "fmt" // Used to print to stdout
// A Struct to represent coins in various currencies
type Coin struct { // NOTE: Structs are like objects that only have attributes
value int // How much the coin is worth in cents as an int
name string // The colloquial name of the coin
}
// Creates all the coins found in CAD currency, adds them to a list then iterates and prints their values
func main() {
// All CAD coins NOTE: Value is in cents so for example a toonie($2) is 200
var coins []Coin // Instantiate a slice of unknown length, containing coins
toonie := Coin{value: 200, name: "Toonie"}
loonie := Coin{value: 100, name: "Loonie"}
quarter := Coin{value: 25, name: "Quarter"}
dime := Coin{value: 10, name: "Dime"}
nickle := Coin{value: 5, name: "Nickle"}
coins = append(coins, toonie, loonie, quarter, dime, nickle) // Fill the slice with the Coin's
fmt.Printf("The coins in CAD are:\n")
for _, currentCoin := range coins { // For loop to print coin information
fmt.Printf("Coin name: %v \nCoin Value (in cents): %v\n",
currentCoin.name, currentCoin.value)
}
}
package main // Allows file to be run on it's own
import "fmt" // Used to print to stdout
// Coin is a Struct to represents coins in various currencies
type Coin struct { // NOTE: Structs are like objects that only have attributes
value float64 // How much the coin is worth
name string // The colloquial name of the coin
}
// Currency is an interface that stores Currency conversion functions
type Currency interface {
toUSD() float64
}
// toUSD takes a coin in CAD and returns the USD equivalent
func (c Coin) toUSD() float64 {
return c.value * 0.7 // 1 CAD is roughly .7 USD
}
// Instantiates a coin and runs the toUSD function
func main() {
toonie := Coin{value: 2.00, name: "Toonie"}
fmt.Printf("One %v is worth CAD $%v and USD $%v",
toonie.name, toonie.value, toonie.toUSD())
}
package main // Allows file to be run on it's own
import "fmt" // Used to print to stdout
// Currency is a dummy type for the enumerator found below
type Currency int
// An enumerator of various currencies
const (
// NOTE: iota starts at 0 and increments on every definition until the next const()
CAD Currency = iota // CAD == 0
USD Currency = iota // USD == 1
)
// Function takes a Currency as an argument and returns a string representation
func (currency Currency) String() string {
// Define an array of strings to print based on the enumerator definition
names := [...]string{
"CAD", // If the currency passed is equal to 0 return CAD
"USD"} // If the currency passed is equal to 1 return USD
return names[currency] // return the name of currency
}
// Prints the string and int representations of Currency values
func main() {
fmt.Printf("%s is equivalent to %d\n%s is equivalent to %d",
Currency(0), Currency(0), Currency(1), Currency(1))
}